/*
 * Copyright (c) 2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
 
#include "cast_engine_common.h"
#include "dlna_stream_listener_impl.h"
#include "dlna_listener_impl.h"
#include "dlna_player_listener.h"
#include "dlna_controller.h"
#include "cast_engine_dlna_service.h"
#include "display_manager.h"
#include "cast_engine_log.h"
#include "parameters.h"
#include "parameter.h"
#include "ability_manager_client.h"
#include <thread>
#include <unistd.h>


using namespace OHOS;
using namespace OHOS::Rosen;
using namespace OHOS::CastEngine;
using namespace OHOS::CastEngine::CastEngineService;
using namespace OHOS::CastEngine::CastEngineClient;

constexpr const char* DLNA_SYSTEM_PARAM_ENABLE = "persist.dlnadmr.enable";
constexpr const char* DLNA_SYSTEM_PARAM_FRIENDLY_NAME = "persist.dlnadmr.friendlyname";
constexpr int COMMON_EVENT_NET_CONNECTIVITY_CONNECTED = 3;
constexpr int COMMON_EVENT_NET_CONNECTIVITY_DISCONNECTED = 5;

DEFINE_CAST_ENGINE_LABEL("Cast-Engine-Dlna-Service");


REGISTER_SYSTEM_ABILITY_BY_ID(CastEngineDlnaService, 1603, true);

CastEngineDlnaService::CastEngineDlnaService(int32_t saId, bool runOnCreate) : SystemAbility(saId, runOnCreate)
{

}

CastEngineDlnaService::~CastEngineDlnaService()
{

}

sptr<Surface> CastEngineDlnaService::GetSubWindowSurface()
{
    sptr<Rosen::WindowOption> option = new Rosen::WindowOption();
    auto defaultDisplay = OHOS::Rosen::DisplayManager::GetInstance().GetDefaultDisplay();
    int width = defaultDisplay->GetWidth();
    int height = defaultDisplay->GetHeight();

    option->SetWindowRect({ 0, 0, width, height });
    option->SetWindowType(Rosen::WindowType::WINDOW_TYPE_APP_LAUNCHING);
    option->SetWindowMode(Rosen::WindowMode::WINDOW_MODE_FLOATING);

    previewWindow_ = Rosen::Window::Create(DLNA_SURFACE_NAME, option);
    if (previewWindow_ == nullptr || previewWindow_->GetSurfaceNode() == nullptr) {
         CLOGE("create Window failed");
        return nullptr;
    }
       
    std::shared_ptr<OHOS::Rosen::RSSurfaceNode> surfaceNode = previewWindow_->GetSurfaceNode();
    surfaceNode->SetFrameGravity(Rosen::Gravity::RESIZE);

    //Rosen::RSTransaction::FlushImplicitTransaction();
    
    return surfaceNode->GetSurface();
}

int32_t CastEngineDlnaService::OnPositionChanged(int position, int duration)
{
    int32_t result = -1; 
    if (!dlnaClient_) {
         CLOGE("dlnaClient_ is null");
        return result;
    }
    dlnaClient_->OnPositionChanged(position, duration, result);
    return result;
}


int32_t CastEngineDlnaService::OnStateChanged(const PlayerStates playbackState)
{
    int32_t result = -1; 

    if ((previewWindow_ != nullptr || isAppRuning_) && playbackState == PlayerStates::PLAYER_IDLE) {
        dlnaPlayer_->Prepare();
    }

    if (!dlnaClient_) {
         CLOGE("dlnaClient_ is null");
        return result;
    }
    dlnaClient_->OnPlayerStatusChanged(static_cast<int>(playbackState), result);
    return result;
}

int32_t CastEngineDlnaService::OnVolumeChanged(int volume, int maxVolume)
{
        int32_t result = -1; 
    if (!dlnaClient_) {
         CLOGE("dlnaClient_ is null");
        return result;
    }
    if(dlnaClient_->sendVolumeChangedEvent){
        dlnaClient_->OnVolumeChanged(volume, maxVolume,result);
    }
    
    return result;
}

int32_t CastEngineDlnaService::OnPlayerReady(std::string clientIP)
{    
    int result = -1;

    if(!isAppRuning_) {
        isAppRuning_ = true;
        CLOGI("StartAbility");

        dlnaPlayer_ =  DlnaStreamManager::GetInstance()->CreateDlnaPlayer(nullptr);
        sptr<IDlnaPlayerListener> listenerStub = new (std::nothrow) DlnaPlayerListener(this);
        if (!dlnaPlayer_) {
            CLOGE("DlnaPlayer is null");
        } else {
            dlnaPlayer_->RegisterListener(listenerStub);
        }

        OHOS::AAFwk::Want dlnaAppWant;
        dlnaAppWant.SetElementName(DLNA_APP_BUNDLE_NAME,DLNA_APP_ABILITY_NAME);
        dlnaAppWant.SetParam("clientIp", clientIP);
        CLOGI("want param clientIp:%{public}s",clientIP.c_str());
        ErrCode errCode = OHOS::AAFwk::AbilityManagerClient::GetInstance()->StartAbility(dlnaAppWant);
         if (errCode != ERR_OK) {
            CLOGE("AppDetailAbility start ability failed, errCode: %{public}d",
                errCode);

            sptr<Surface> producerSurface = GetSubWindowSurface();
            if(!producerSurface) {
                CLOGE("producerSurface is null");
                return result;
            }
            producerSurface->SetUserData("SURFACE_FORMAT", std::to_string(12));
            
            sptr<IBufferProducer> producer = producerSurface->GetProducer();
            if(!producer) {
                CLOGE("producer is null");
                return result;
            }
            int ret =  SetDmrSurface(producer);
            if (ret != 0) {
                CLOGE("SetSurface failed");
                return result;
            }

            previewWindow_->Show();
            result = 0;
        } 
    }
    return result;
}

int32_t CastEngineDlnaService::OnPlayerEnd()
{
    int32_t result = -1;
    if (dlnaPlayer_) {
        DlnaStreamManager::GetInstance()->DestoryDlnaPlayer();
        dlnaPlayer_ = nullptr;
    } 
    if (previewWindow_ != nullptr) {
        CLOGI("Destroy dlnaWindow");
        previewWindow_->Hide();
        previewWindow_->Destroy();
        previewWindow_ = nullptr;
        result = 0;
    }    
    
    isAppRuning_ = false;
    
    if (!dlnaClient_) {
         CLOGE("dlnaClient_ is null");
        return result;
    }

    dlnaClient_->OnPlayerEnd(result);
    dlnaClient_ = nullptr;
    


    return result;
}

int32_t CastEngineDlnaService::SendDmrPlayer(const sptr<IRemoteObject> &object)
{
    int32_t result = -1;
    dlnaClientAppDiedListener_ = new DlnaClientAppDiedListener();
    object->AddDeathRecipient(dlnaClientAppDiedListener_);
    dlnaClient_ = new CastEngineClient::DlnaClientAppProxy(object);

    if (dlnaClient_) {
        result = 0;
    }
    return result;
}

void CastEngineDlnaService::OnStart()
{
    bool ret = Publish(this);
    if (!ret) {
        return;
    }

    std::shared_ptr<CastEngineDlnaEventController> eventController = CastEngineDlnaEventController::GetInstance();

    eventController->Init();
    eventController->RegisterOnConnectvityChange(std::bind(&CastEngineDlnaService::OnConnectvityChange, this, std::placeholders::_1));

    AddWatchParameter();
}

void CastEngineDlnaService::OnStop()
{

}

void CastEngineDlnaService::OnConnectvityChange(int32_t code)
{
    if ( (code == COMMON_EVENT_NET_CONNECTIVITY_CONNECTED || code == COMMON_EVENT_NET_CONNECTIVITY_DISCONNECTED)
                            && dmrState_ == DmrStates::DMR_STATE_AVAILABLE)
    {
        sleep(2);
        DmrClose();
        DmrOpen();
    }
}

void CastEngineDlnaService::AddWatchParameter()
{
    int ret = -1;
    ret = WatchParameter(DLNA_SYSTEM_PARAM_ENABLE, OnDlnaSyStemParamChange, this);
    if (ret != 0) {
        CLOGE("watch parameter %{public}s failed with %{public}d.",DLNA_SYSTEM_PARAM_ENABLE, ret);
    }
    ret = WatchParameter(DLNA_SYSTEM_PARAM_FRIENDLY_NAME, OnDlnaSyStemParamChange, this);
    if (ret != 0) {
        CLOGE("watch parameter %{public}s failed with %{public}d.",DLNA_SYSTEM_PARAM_FRIENDLY_NAME, ret);
    }
}

void CastEngineDlnaService::OnDlnaSyStemParamChange(const char *key, const char *value, void *context)
{
    CastEngineDlnaService *castEngineDlnaService = (CastEngineDlnaService*)context;
    CLOGI("DlnaSyStemParamChange key = %{public}s value = %{public}s",key, value);
    if (!strcmp(key, DLNA_SYSTEM_PARAM_ENABLE)) {
        if (!strcmp(value, "true")) {
            castEngineDlnaService->DmrOpen();
        } else {
            castEngineDlnaService->DmrClose();
        }
    } else if (!strcmp(key, DLNA_SYSTEM_PARAM_FRIENDLY_NAME)) {
        if(value) {
            castEngineDlnaService->SetDlnaDeviceName(value);
        }
    }
}

int32_t CastEngineDlnaService::DmrOpen() 
{
    if (dmrState_ == DmrStates::DMR_STATE_UNAVAILABLE) {
        std::shared_ptr<IDlnaListener> DlnaListener = std::make_shared<DlnaListenerImpl>(this);
        DlnaController::GetInstance().SetDlnaListener(DlnaListener);

        std::shared_ptr<IDlnaStreamListener> listener = std::make_shared<DlnaStreamListenerImpl>();
        DlnaStreamManager::GetInstance()->RegisterCastDlnaListener(listener);

        DlnaController::GetInstance().DmrCreate();

        dmrState_ = DmrStates::DMR_STATE_AVAILABLE;
    }

    return 0;
}

int32_t CastEngineDlnaService::DmrClose() 
{
    if (dmrState_ == DmrStates::DMR_STATE_AVAILABLE) {
        DlnaController::GetInstance().DmrDestroy();
        DlnaStreamManager::GetInstance()->UnRegisterCastDlnaListener();
        OnPlayerEnd();
        if (dlnaPlayer_) {
            dlnaPlayer_->UnregisterListener();
            dlnaPlayer_->Release();
            dlnaPlayer_ = nullptr;
        } 
        dmrState_ = DmrStates::DMR_STATE_UNAVAILABLE;
    }

    return 0;
}

int32_t CastEngineDlnaService::Play() 
{
    int32_t result = -1;
    if (!dlnaPlayer_) {
        CLOGE("DlnaPlayer is null");
        return result;   
    }  
    result = dlnaPlayer_->Play();
    return result;   
}

int32_t CastEngineDlnaService::Pause() 
{
    int32_t result = -1;
    if (!dlnaPlayer_) {
        CLOGE("DlnaPlayer is null");
        return result;   
    }  
    result = dlnaPlayer_->Pause();
    return result;   
}

int32_t CastEngineDlnaService::Stop() 
{
    int32_t result = -1;
    if (!dlnaPlayer_) {
        CLOGE("DlnaPlayer is null");
        return result;   
    }
    result = dlnaPlayer_->Stop();
    result = dlnaPlayer_->UnregisterListener();
    isAppRuning_ = false;
    return result;   
}

int32_t CastEngineDlnaService::Forward(int &seconds) 
{
    int32_t result = -1;
    if (!dlnaPlayer_) {
        CLOGE("DlnaPlayer is null");
        return result;   
    }  
    result = dlnaPlayer_->GetPosition(position_);
    CLOGI("GetPosition is %d",position_);
    result = dlnaPlayer_->Seek(position_+15000);
    return result;   
}

int32_t CastEngineDlnaService::Backward(int &seconds) 
{
    int32_t result = -1;
    if (!dlnaPlayer_) {
        CLOGE("DlnaPlayer is null");
        return result;   
    }  
    result = dlnaPlayer_->GetPosition(position_);
    CLOGI("GetPosition is %d",position_);
    result = dlnaPlayer_->Seek(position_-15000);
    return result;   
}

int32_t CastEngineDlnaService::GetPosition(int &position) 
{
    int32_t result = -1;
    if (!dlnaPlayer_) {
        CLOGE("DlnaPlayer is null");
        return result;   
    }  
    result = dlnaPlayer_->GetPosition(position);
    return result;   
}

int32_t CastEngineDlnaService::GetPlayerStatus(int32_t &playerStates) 
{
    int32_t result = -1;
    PlayerStates status;
    result = dlnaPlayer_->GetPlayerStatus(status);
    playerStates = static_cast<int32_t>(status);
    return result;   
}

int32_t CastEngineDlnaService::GetDuration(int &duration) 
{
    int32_t result = -1;
    if (!dlnaPlayer_) {
        CLOGE("DlnaPlayer is null");
        return result;   
    }  
    result = dlnaPlayer_->GetDuration(duration);
    return result;   
}

int32_t CastEngineDlnaService::Seek(int position) 
{
    int32_t result = -1;
    if (!dlnaPlayer_) {
        CLOGE("DlnaPlayer is null");
        return result;   
    }
    CLOGD("Seek mseconds is: %{public}d",position);
    result = dlnaPlayer_->Seek(position);
    return result;   
}

int32_t CastEngineDlnaService::SetDmrSurface(sptr<IBufferProducer> producer)
{
    int32_t result = -1;

    sptr<Surface> pSurface = Surface::CreateSurfaceAsProducer(producer);
    if (!pSurface) {
        return result;
    }
    result = dlnaPlayer_->SetSurface_ext(pSurface);
    if (result == 0) {
        CLOGE("SetSurface successed");
        dlnaPlayer_->Prepare();
    } else {
        CLOGE("SetSurface faild");
        dlnaClient_->OnPlayerEnd(result);
    }
    return result;
}

int32_t CastEngineDlnaService::SetSurfaceId(std::string surfaceId)
{
    int32_t result = -1;

    return result;
}

int32_t CastEngineDlnaService::GetDlnaDeviceName(std::string &deviceName)
{
    int32_t result = 0;

    DlnaController::GetInstance().GetDlnaDeviceName(deviceName);
    CLOGD("GetDlnaDeviceName:%{public}s",deviceName.c_str());

    return result;
}

int32_t CastEngineDlnaService::SetDlnaDeviceName(const std::string &deviceName)
{

    int32_t result = -1;
    CLOGD("SetDlnaDeviceName:%{public}s",deviceName.c_str());
    result = DlnaController::GetInstance().SetDlnaDeviceName(deviceName);

    return result;
}

int32_t CastEngineDlnaService::GetDmrState(DmrStates &dmrState)
{
    int32_t result = 0;

    dmrState = dmrState_;

    return result;
}

int32_t CastEngineDlnaService::GetVolume(int &volume, int &maxVolume)
{
    int32_t result = -1;
    result = dlnaPlayer_->GetVolume(volume,maxVolume);
    return result;
}

int32_t CastEngineDlnaService::SetVolume(int volume)
{
    int32_t result = -1;
    result = dlnaPlayer_->SetVolume(volume);
    return result;
}

int32_t CastEngineDlnaService::AuthDeviceFail()
{
    dlnaClient_ = nullptr;
    DmrClose();
    DmrOpen();
    return 0;
}